package tsdb

import (
	"encoding/json"
	"io"
	"os"
	"path"
	"strings"
	"sync"
)

type TSDB struct {
	path   string
	tables map[string]*os.File
	mu     sync.RWMutex
}

func (this *TSDB) getTable(name string) (table *os.File, err error) {
	this.mu.RLock()
	table, ok := this.tables[name]
	this.mu.RUnlock()
	if !ok {
		if err := os.MkdirAll(this.path, 0777); err != nil {
			return nil, err
		}
		table, err = os.OpenFile(this.path+name+".db", os.O_CREATE|os.O_APPEND, 0777)
		if err != nil {
			return nil, err
		}
		table, err = os.OpenFile(this.path+name+"_index.db", os.O_CREATE|os.O_RDWR, 0777)
		if err != nil {
			return nil, err
		}
		this.mu.Lock()
		this.tables[name] = table
		this.mu.Unlock()
	}
	return table, nil
}

func newTSDB(filePath string) *TSDB {
	return &TSDB{
		path:   "./" + path.Dir(filePath) + "/",
		tables: make(map[string]*os.File),
	}
}

func (this *TSDB) Set(tableName string, key string, value map[string]interface{}) error {
	table, err := this.getTable(tableName)
	if err != nil {
		return err
	}
	bs, err := json.Marshal(value)
	if err != nil {
		return err
	}
	_, err = table.WriteString(key + ":" + string(bs) + "\n")
	return err
}

func (this *TSDB) Get(tableName string, key string) (map[string]interface{}, bool, error) {
	table, err := this.getTable(tableName)
	if err != nil {
		return nil, false, err
	}

	read := func(offset *int64, mark byte) (string, error) {
		bytes := []byte(nil)
		for {
			b := make([]byte, 1)
			_, err := table.ReadAt(b, *offset)
			if err != nil {
				return "", err
			}
			*offset++
			if b[0] == mark {
				return string(bytes), nil
			}
			bytes = append(bytes, b[0])
		}
	}
	offset := int64(0)
	for {
		s, err := read(&offset, '\n')
		if err != nil {
			if err == io.EOF {
				return nil, false, nil
			}
			return nil, false, err
		}
		if list := strings.SplitN(s, ":", 2); len(list) >= 2 {
			if list[0] == key {
				m := map[string]interface{}(nil)
				err = json.Unmarshal([]byte(list[1]), &m)
				return m, true, err
			}
		}
	}

}
